/********************************************************************
 *@brief Define property classes and provide property registration methods
 *@author wcf
 *@date 2025-01-10
********************************************************************/

#ifndef __CS_PROPERTY_H__
#define __CS_PROPERTY_H__

#include "csvariant.h"

CS_NAMESPACE_BEGIN

class CSCORE_EXPORT csGenericProperty
{
public:
	inline csGenericProperty(const char* name, const void* object)
		: m_name(name), m_object(object) {}
	inline const char* name() const { return m_name; }
	inline void* object() const { return const_cast<void*>(m_object); }
	virtual csVariant value() const = 0;
	virtual void setValue(const csVariant& value) = 0;
private:
	const char* m_name = nullptr;
	const void* m_object = nullptr;
};

template
<
	typename ObjectType,
	typename ReadType,
	typename ValueType
>
class csReadOnlyPropery : public csGenericProperty
{
public:
	inline csReadOnlyPropery(ReadType read, const char* name, const void* object)
		: csGenericProperty(name, object), m_read(read) {}
	inline csVariant value() const override
	{
		if (!m_read)
		{
			//Property read method is null
			return csVariant();
		}
		ObjectType* obj = reinterpret_cast<ObjectType*>(object());
		if (!obj)
		{
			//The object to which the property belongs is null
			return csVariant();
		}
		return csVariant::fromValue((obj->*m_read)());
	}
	inline void setValue(const csVariant& value) override
	{
		CS_UNUSED(value);
	}
private:
	ReadType m_read;
};

#define CS_REG_READONLY_PROPERTY(\
	ObjectType,\
	Name,\
	ReadValueType,\
	Read)\
	{\
		using ReadType = ReadValueType (ObjectType::*)() const;\
		registerProperty(#Name, new csReadOnlyPropery<ObjectType, ReadType, ReadValueType>(&ObjectType::Read, #Name, this));\
	}

template
<
	typename ObjectType,
	typename WriteType,
	typename ValueType
>
class csWriteOnlyPropery : public csGenericProperty
{
public:
	inline csWriteOnlyPropery(WriteType write, const char* name, const void* object)
		: csGenericProperty(name, object), m_write(write) {}
	inline csVariant value() const override
	{
		return csVariant();
	}
	inline void setValue(const csVariant& value) override
	{
		if (!m_write)
		{
			//Property write method is null
			return;
		}
		ObjectType* obj = reinterpret_cast<ObjectType*>(object());
		if (!obj)
		{
			//The object to which the property belongs is null
			return;
		}
		(obj->*m_write)(value.value<ValueType>());
	}
private:
	WriteType m_write;
};

#define CS_REG_WRITEONLY_PROPERTY(\
	ObjectType,\
	Name,\
	WriteValueType,\
	Write)\
	{\
		using WriteType = void (ObjectType::*)(WriteValueType);\
		registerProperty(#Name, new csWriteOnlyPropery<ObjectType, WriteType, WriteValueType>(&ObjectType::Write, #Name, this));\
	}

template
<
	typename ObjectType,
	typename ReadType,
	typename WriteType,
	typename ValueType
>
class csProperty : public csGenericProperty
{
public:
	inline csProperty(ReadType read, WriteType write, const char* name, const void* object)
		: csGenericProperty(name, object), m_read(read), m_write(write) {}
	inline csVariant value() const override
	{
		if (!m_read)
		{
			//Property read method is null
			return csVariant();
		}
		ObjectType* obj = reinterpret_cast<ObjectType*>(object());
		if (!obj)
		{
			//The object to which the property belongs is null
			return csVariant();
		}
		return csVariant::fromValue((obj->*m_read)());
	}
	inline void setValue(const csVariant& value) override
	{
		if (!m_write)
		{
			//Property write method is null
			return;
		}
		ObjectType* obj = reinterpret_cast<ObjectType*>(object());
		if (!obj)
		{
			//The object to which the property belongs is null
			return;
		}
		(obj->*m_write)(value.value<ValueType>());
	}
private:
	ReadType m_read;
	WriteType m_write;
};

#define CS_REG_PROPERTY(\
	ObjectType,\
	Name,\
	ReadValueType,\
	Read,\
	WriteValueType,\
	Write)\
	{\
		using ReadType = ReadValueType (ObjectType::*)() const;\
		using WriteType = void (ObjectType::*)(WriteValueType);\
		registerProperty(#Name, new csProperty<ObjectType, ReadType, WriteType, ReadValueType>(&ObjectType::Read, &ObjectType::Write, #Name, this));\
	}

CS_NAMESPACE_END

#endif // !__CS_PROPERTY_H__
